#ifndef LOGGER_H
#define LOGGER_H
#ifdef ignore
#undef ignore
#endif
#include <stdarg.h>
#include <string>
#include <string.h>
#include <fstream>
#include <stdint.h>
#include <pthread.h>

#define LOG_EMERG             0
#define LOG_ALERT             1
#define LOG_CRIT              2
#define LOG_ERR               3
#define LOG_WARNING           4
#define LOG_NOTICE            5
#define LOG_INFO              6
#define LOG_DEBUG             7
#define LOG_USE_GLOBAL_FILTER 8

#define LoggerPtr Logger::ptr()

inline const char *shortname(const char *filename)
{ 
    return strrchr(filename, '/') != 0 ? strrchr(filename, '/')+1 : filename;
}

class Logger
{
public:
    static Logger *ptr();
    
    bool initialize();
    bool unintialize();
    bool SetLogLevel(int32_t log_Level);
    int32_t logMessageFilter();    

    void LogEmergency(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogAlert(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogCritical(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogError(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogWarning(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogNotice(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogInfo(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogDebug(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...);
    void LogByteDump(const char* filename, 
                    uint32_t lineNumber, 
                    const int32_t logLevel,
                    const uint8_t *bytes,
                    const int32_t numberOfBytes);
    void LogByteDump(const char* filename, 
                    uint32_t lineNumber, 
                    const int32_t logLevel,
                    const char *bytes,
                    const int32_t numberOfBytes);
    void lock();
    void unlock();
   
    


private:
    Logger();
    Logger(const Logger&);
    Logger& operator=(const Logger&);
    bool RollOverLog();  
    void LogToStdout(const char* filename, uint32_t lineNumber, const int32_t logLevel, const char *const originalLogMessage);
    void LogToStream(const char* filename, uint32_t lineNumber, const int32_t logLevel, const char *const originalLogMessage);
    void LogMessage(const char* filename, uint32_t lineNumber, const int32_t logLevel, const char *const originalLogMessage);
    static Logger *InstanceM;
    pthread_mutex_t counter_mutex;
    int32_t CurrentLogLine;
    int32_t MaxLogLine;
    uint32_t MessageCouter;
    
    std::string ComponentNameM;
    std::string LogFileNameM;
    bool InitializedM;
    bool ShouldLogToScreen;
    bool ShouldLogToFile;
    bool RollOverLogM;  

    int32_t CountLog[LOG_USE_GLOBAL_FILTER];
    std::ofstream  LogStream;
     
    int32_t LogLevel;
    enum{ LogBufferLengthM = 4096 };
    char OriginalLogMessage[LogBufferLengthM];
    
    uint32_t pidM;

};


inline void Logger::lock()
{
    pthread_mutex_lock(&counter_mutex);
}

inline void Logger::unlock()
{
    pthread_mutex_unlock(&counter_mutex);
}

#define LOGGER_LOG_METHOD_BODY(logLevel)\
    va_list ap;\
    va_start(ap, msgFormat);\
    vsnprintf(OriginalLogMessage, LogBufferLengthM, msgFormat, ap);\
    va_end(ap);\
    ++CountLog[logLevel];\
    if (!InitializedM)\
    {\
        LogToStdout(filename, lineNumber, logLevel, OriginalLogMessage);\
        return;\
    }\
    LogMessage(filename, lineNumber, logLevel, OriginalLogMessage)

       

inline int Logger::logMessageFilter()
{
    return LogLevel;
}

inline Logger *Logger::ptr()
{
    Logger *Pointer = InstanceM;
    if ( InstanceM == NULL )
    {
        Pointer = InstanceM = new Logger();
    }
    return Pointer;
}
 
inline void Logger::LogEmergency(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{ 
    LOGGER_LOG_METHOD_BODY(LOG_EMERG);
}

inline void Logger::LogAlert(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_ALERT);
}

inline void Logger::LogCritical(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_CRIT);    
}

inline void Logger::LogError(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_ERR);
}

inline void Logger::LogWarning(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_WARNING);
}

inline void Logger::LogNotice(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_NOTICE);
}

inline void Logger::LogInfo(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_INFO);
}

inline void Logger::LogDebug(const char *filename, uint32_t lineNumber, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_DEBUG);
}

#define LogEmergency(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_EMERG) LoggerPtr->LogEmergency(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock()

#define LogAlert(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_ALERT) LoggerPtr->LogAlert(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogCritical(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_CRIT) LoggerPtr->LogCritical(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogError(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_ERR) LoggerPtr->LogError(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogWarning(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_WARNING) LoggerPtr->LogWarning(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogNotice(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_NOTICE) LoggerPtr->LogNotice(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogInfo(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_INFO) LoggerPtr->LogInfo(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();

#define LogDebug(fmt, args...) LoggerPtr->lock();\
if (LoggerPtr->logMessageFilter() >= LOG_DEBUG) LoggerPtr->LogDebug(shortname(__FILE__), __LINE__, (char *)fmt, ##args);\
LoggerPtr->unlock();
#endif



